#include <QtWidgets>
#include <QtGlobal>
#include <cstdlib>

#include <Qsci/qscilexerpython.h>
#include <Qsci/qsciapis.h>

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QJsonObject* configOb, QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow){
    setWindowIcon(QIcon(":/manim-studio.ico"));
    MainWindow::configOb = configOb;
    createActions();
    QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
    const QByteArray geometry = settings.value("geometry", QByteArray()).toByteArray();
    if (geometry.isEmpty()) {
        const QRect availableGeometry = screen()->availableGeometry();
        resize(availableGeometry.width() / 2, availableGeometry.height() / 2);
        move((availableGeometry.width() - width()) / 2,
             (availableGeometry.height() - height()) / 2);
    } else {
        restoreGeometry(geometry);
    }

#ifndef QT_NO_SESSIONMANAGER
    QGuiApplication::setFallbackSessionManagementEnabled(false);
    connect(qApp, &QGuiApplication::commitDataRequest,
            this, &MainWindow::commitData);
#endif

    setCurrentFile(QString());
    setUnifiedTitleAndToolBarOnMac(true);
}

void MainWindow::createActions(){
    ui->setupUi(this);
    textEdit = new QsciScintilla;
    setHilighter();
    setCentralWidget(textEdit);

    ui->timeLineDock->setWidget(ui->widget_3);
    ui->widget_3->setLayout(ui->timeLine);
    QWidget *none = new QWidget;
    ui->timeLineDock->setTitleBarWidget(none);

    ui->toolBoxDock->setWidget(ui->toolBox);
    ui->toolBoxDock_2->setWidget(ui->toolBox_2);

// new
    ui->newAct->setShortcuts(QKeySequence::New);
    connect(ui->newAct, &QAction::triggered, this, [&]{
        if (maybeSave()) {
            textEdit->clear();
            setCurrentFile(QString());
        }
    });   // I used lambada expression to simplify.

// open
    ui->openAct->setShortcuts(QKeySequence::Open);
    connect(ui->openAct, &QAction::triggered, this, [&]{
        if (maybeSave()) {
            QString fileName = QFileDialog::getOpenFileName(this);
            if (!fileName.isEmpty())
                loadFile(fileName);
        }
    });

// save
    ui->saveAct->setShortcuts(QKeySequence::Save);
    connect(ui->saveAct, &QAction::triggered, this, [&]{
            if (curFile.isEmpty()) saveAs();
            else save(curFile);
    });

// saveas
    connect(ui->saveAsAct, &QAction::triggered, this, &MainWindow::saveAs);
    ui->saveAsAct->setShortcuts(QKeySequence::SaveAs);

// exit
    connect(ui->exitAct, &QAction::triggered, this, &QWidget::close);
    ui->exitAct->setShortcuts(QKeySequence::Quit);

// paste cut copy
    connect(ui->cutAct, SIGNAL(triggered()), textEdit, SLOT(cut()));
    connect(ui->copyAct, SIGNAL(triggered()), textEdit, SLOT(copy()));
    connect(ui->pasteAct, SIGNAL(triggered()), textEdit, SLOT(paste()));

// init
    connect(ui->initAct, &QAction::triggered, this, [&]{
        textEdit->clear();
        textEdit->setText("from manimlib.imports import *\n"
                               "\n"
                               "    class default(Scene):\n"
                               "        def construct(self):\n");
    });

#ifndef QT_NO_CLIPBOARD

// cut, copy, paste
    ui->cutAct->setShortcuts(QKeySequence::Cut);
    ui->copyAct->setShortcuts(QKeySequence::Copy);
    ui->pasteAct->setShortcuts(QKeySequence::Paste);

#endif // !QT_NO_CLIPBOARD

// make
    connect(ui->makeAct, &QAction::triggered, this, [&]{
        MakePage *makepage = new MakePage;
        makepage->show();
        command("",choseScene());
    });

// debug
    connect(ui->debugAct, &QAction::triggered, this, [&]{
        if (maybeSave()){
            command(configOb->value("debugcommand").toString(),choseScene());
        }
    });

// edittex
    connect(ui->editAct, &QAction::triggered,this,[&]{
        TexEdit *texEdit = new TexEdit("Haven't connect it to target content!");
        texEdit->show();
    });

// about
     connect(ui->aboutAct, &QAction::triggered, this, [&]{
        QMessageBox::about(this, tr("About Manim Studio"),
                 tr("<b>Manim Studio</b> is an integrated environment for creating "
                    "animation using Manim. This is the test edition."));
    });

// tutorial
    connect(ui->tutorialAct, &QAction::triggered, this, [&]{
        QString url = QCoreApplication::applicationDirPath().left(QCoreApplication::applicationDirPath().lastIndexOf("/"));
        QDesktopServices::openUrl(QUrl(url+QString("/doc/index.html")));
    });

// setting
    menuBar()->addAction(tr("&Settings"),this,[&]{
        SettingPage *page = new SettingPage(configOb);
        page->show();
    });

#ifndef QT_NO_CLIPBOARD
    ui->cutAct->setEnabled(false);
    ui->copyAct->setEnabled(false);
    connect(textEdit, SIGNAL(copyAvailable(bool)), ui->cutAct, SLOT(setEnabled(bool)));
    connect(textEdit, SIGNAL(copyAvailable(bool)), ui->copyAct, SLOT(setEnabled(bool)));
#endif // !QT_NO_CLIPBOARD

    statusBar()->showMessage(tr("Ready"));
}

bool MainWindow::maybeSave(){
    if (!textEdit->isModified())
        return true;
    const QMessageBox::StandardButton ret
        = QMessageBox::warning(this, tr("Manim Studio"),
                               tr("The document has been modified.\n"
                                  "Do you want to save your changes?"),
                               QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
    switch (ret) {
    case QMessageBox::Save:
        if (curFile.isEmpty()) {return saveAs();} else {return save(curFile);}
    case QMessageBox::Cancel:
        return false;
    default:
        break;
    }
    return true;
}

void MainWindow::loadFile(const QString &fileName){
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("Manim Studio"),
                             tr("Cannot read file %1:\n%2.")
                             .arg(QDir::toNativeSeparators(fileName), file.errorString()));
        return;
    }

    QTextStream in(&file);
#ifndef QT_NO_CURSOR
    QGuiApplication::setOverrideCursor(Qt::WaitCursor);
#endif
    textEdit->setText(in.readAll());
#ifndef QT_NO_CURSOR
    QGuiApplication::restoreOverrideCursor();
#endif

    setCurrentFile(fileName);
    statusBar()->showMessage(tr("File loaded"), 2000);
}

bool MainWindow::save(const QString &fileName){
    QString errorMessage;

    QGuiApplication::setOverrideCursor(Qt::WaitCursor);
    QSaveFile file(fileName);
    if (file.open(QFile::WriteOnly | QFile::Text)) {
        QTextStream out(&file);
        out << textEdit->text();
        if (!file.commit()) {
            errorMessage = tr("Cannot write file %1:\n%2.")
                           .arg(QDir::toNativeSeparators(fileName), file.errorString());
        }
    } else { errorMessage = tr("Cannot open file %1 for writing:\n%2.")
                       .arg(QDir::toNativeSeparators(fileName), file.errorString());
    }
    QGuiApplication::restoreOverrideCursor();

    if (!errorMessage.isEmpty()) {
        QMessageBox::warning(this, tr("Manim Studio"), errorMessage);
        return false;
    }

    setCurrentFile(fileName);
    statusBar()->showMessage(tr("File saved"), 2000);
    return true;
}

void MainWindow::setCurrentFile(const QString &fileName){
    curFile = fileName;
    textEdit->setModified(false);
    setWindowModified(false);

    QString shownName = curFile;
    if (curFile.isEmpty())
        shownName = "untitled.py";
    setWindowFilePath(shownName);
}

QString MainWindow::strippedName(const QString &fullFileName){
    return QFileInfo(fullFileName).fileName();
}

#ifndef QT_NO_SESSIONMANAGER
void MainWindow::commitData(QSessionManager &manager)
{
    if (manager.allowsInteraction()) {
        if (!maybeSave())
            manager.cancel();
    } else {
        // Non-interactive: save without asking
        if (textEdit->isModified()){
            if (curFile.isEmpty()) saveAs();
            else save(curFile);
        }
    }
}
#endif

void MainWindow::closeEvent(QCloseEvent *event){
    if (maybeSave()) {
        QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
        settings.setValue("geometry", saveGeometry());
        event->accept();
    } else {
        event->ignore();
    }
}

bool MainWindow::saveAs(){
    QFileDialog dialog(this);
    dialog.setWindowModality(Qt::WindowModal);
    dialog.setAcceptMode(QFileDialog::AcceptSave);
    if (dialog.exec() != QDialog::Accepted)
        return false;
    return save(dialog.selectedFiles().first());
}

void MainWindow::command(QString cmd,QString scenename){
    if (scenename.compare("-")!=0){
        int index = curFile.lastIndexOf("/");
        QString workPath = curFile.left(index);
        QString fileName = curFile.right(curFile.length()-index-1);
        QByteArray cmds;
#ifdef Q_OS_UNIX
        QString command = "gnome-terminal -- bash -c \"cd "+workPath+"&&"+cmd.arg(fileName).arg(scenename)+";"
                          "read -n 1 -p \"Press any button to continue...\"\"";
#endif
#ifdef Q_OS_WIN
        QString command = "cmd /c \"cd /d"+workPath+"&&"+cmd.arg(fileName).arg(scenename)+"&pause \"";
#endif
        system(cmds.append(command).data());
    }
}

void MainWindow::setHilighter(){
    QsciLexerPython *textLexer = new QsciLexerPython;
    textEdit->setLexer(textLexer);

    textEdit->setMarginType(0,QsciScintilla::NumberMargin);
    textEdit->setMarginLineNumbers(0,true);
    textEdit->setMarginWidth(0,15);

    QsciAPIs *apis = new QsciAPIs(textLexer);
    apis->load(QString(":/keywords.txt"));
    apis->prepare();
    textEdit->setAutoCompletionSource(QsciScintilla::AcsAll);
    textEdit->setAutoCompletionCaseSensitivity(true);
    textEdit->setAutoCompletionThreshold(2);

    textEdit->setAutoIndent(true);
    textEdit->setIndentationsUseTabs(false);
    textEdit->setTabWidth(4);
}

QString MainWindow::choseScene(){
    int index = 0;
    short int count = 0;
    QComboBox *sceneCoBox = new QComboBox;
    QString scene;
    while (index!=-1) {
        index += 6;
        index = textEdit->text().indexOf("class ",index);
        if (index!=-1){
            int indexs = textEdit->text().indexOf("(",index+6);
            scene = textEdit->text().mid(index+6,indexs-index-6);
            sceneCoBox->addItem(scene);
            count += 1;
        }
    }
    if (count==0){
        QMessageBox::warning(this,tr("Manim Studio"),tr("Failed because there isn\'t any scene in the file!"));
        return "-";
    } else {
        if (count!=1){
            QDialog *ask = new QDialog;
            QLabel chose(tr("Chose a class to create viedo"));
            QVBoxLayout *vLayout = new QVBoxLayout;
            vLayout->addWidget(&chose);
            vLayout->addWidget(sceneCoBox);
            QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
            vLayout->addWidget(buttonBox);
            ask->setLayout(vLayout);
            ask->show();
            connect(buttonBox,&QDialogButtonBox::accepted,ask,&QDialog::accept);
            connect(buttonBox,&QDialogButtonBox::rejected,ask,&QDialog::reject);
            if (ask->exec()==QDialog::Accepted){
                return sceneCoBox->currentText();
            } else {
                return "-";
            }
        } else {
            return sceneCoBox->currentText();
        }
    }
}

MainWindow::~MainWindow(){
    delete ui;
}
